package com.lifeverse.aspect;

import com.lifeverse.entity.enums.AuditAction;
import com.lifeverse.entity.enums.AuditResult;
import com.lifeverse.service.AuditLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

/**
 * 审计日志切面
 * 自动记录标注了@Auditable注解的方法的审计日志
 */
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class AuditLogAspect {
    
    private final AuditLogService auditLogService;
    
    /**
     * 审计注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Auditable {
        /**
         * 操作类型
         */
        AuditAction action();
        
        /**
         * 资源类型
         */
        String resourceType() default "";
        
        /**
         * 操作描述
         */
        String description() default "";
        
        /**
         * 是否为敏感操作
         */
        boolean sensitive() default false;
        
        /**
         * 风险等级（1-5）
         */
        int riskLevel() default 1;
    }
    
    /**
     * 环绕通知，记录审计日志
     */
    @Around("@annotation(auditable)")
    public Object auditLog(ProceedingJoinPoint joinPoint, Auditable auditable) throws Throwable {
        long startTime = System.currentTimeMillis();
        String userId = getCurrentUserId();
        String username = getCurrentUsername();
        HttpServletRequest request = getCurrentRequest();
        
        String resourceType = auditable.resourceType();
        String resourceId = extractResourceId(joinPoint);
        String description = buildDescription(auditable.description(), joinPoint);
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 记录成功的审计日志
            long duration = System.currentTimeMillis() - startTime;
            
            if (request != null) {
                auditLogService.logHttpRequest(
                        request, userId, username, auditable.action(),
                        resourceType, resourceId, description,
                        AuditResult.SUCCESS, null, duration, 200
                );
            } else {
                auditLogService.logSuccess(userId, username, auditable.action(),
                        resourceType, resourceId, description);
            }
            
            return result;
            
        } catch (Exception e) {
            // 记录失败的审计日志
            long duration = System.currentTimeMillis() - startTime;
            String errorMessage = e.getMessage();
            
            if (request != null) {
                auditLogService.logHttpRequest(
                        request, userId, username, auditable.action(),
                        resourceType, resourceId, description,
                        AuditResult.FAILURE, errorMessage, duration, 500
                );
            } else {
                auditLogService.logFailure(userId, username, auditable.action(),
                        resourceType, resourceId, description, errorMessage);
            }
            
            throw e;
        }
    }
    
    /**
     * 获取当前用户ID
     */
    private String getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated() && 
            !"anonymousUser".equals(authentication.getPrincipal())) {
            return authentication.getName();
        }
        return "SYSTEM";
    }
    
    /**
     * 获取当前用户名
     */
    private String getCurrentUsername() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated() && 
            !"anonymousUser".equals(authentication.getPrincipal())) {
            return authentication.getName();
        }
        return "SYSTEM";
    }
    
    /**
     * 获取当前HTTP请求
     */
    private HttpServletRequest getCurrentRequest() {
        ServletRequestAttributes attributes = 
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }
    
    /**
     * 提取资源ID
     */
    private String extractResourceId(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            // 尝试从第一个参数中提取ID
            Object firstArg = args[0];
            if (firstArg instanceof String) {
                return (String) firstArg;
            } else if (firstArg instanceof Long) {
                return firstArg.toString();
            } else if (firstArg != null) {
                // 尝试通过反射获取ID字段
                try {
                    Method getIdMethod = firstArg.getClass().getMethod("getId");
                    Object id = getIdMethod.invoke(firstArg);
                    return id != null ? id.toString() : null;
                } catch (Exception e) {
                    // 忽略异常，返回null
                }
            }
        }
        return null;
    }
    
    /**
     * 构建操作描述
     */
    private String buildDescription(String template, ProceedingJoinPoint joinPoint) {
        if (template.isEmpty()) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            return signature.getMethod().getName();
        }
        
        // 可以在这里实现模板替换逻辑
        // 例如：${methodName}, ${className}, ${args[0]} 等
        String description = template;
        
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        description = description.replace("${methodName}", signature.getMethod().getName());
        description = description.replace("${className}", signature.getDeclaringType().getSimpleName());
        
        // 替换参数占位符
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            String placeholder = "${args[" + i + "]}";
            if (description.contains(placeholder)) {
                String argValue = args[i] != null ? args[i].toString() : "null";
                description = description.replace(placeholder, argValue);
            }
        }
        
        return description;
    }
}